package com.tobeface.wechat.message.verifier;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.tobeface.modules.lang.Langs;
import com.tobeface.wechat.core.WeChatSettings;
import com.tobeface.wechat.message.WeChatMessageCredential;
import com.tobeface.wechat.message.WeChatMessageVerifyService;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;

/**
 * @author loudyn
 */
public final class Sha1WeChatMessageVerifyService implements WeChatMessageVerifyService {

    private static final char[] DIGITS_UPPER = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };

    @Override
    public boolean verify(WeChatSettings settings, WeChatMessageCredential credential) {

        List<String> bases = ImmutableList.of(
                settings.getAppToken(),
                credential.getTimestamp(),
                credential.getNonce()
        );

        String basestr = Joiner.on("").join(Ordering.natural().immutableSortedCopy(bases));
        return verifySignature(credential.getSignature(), basestr);
    }

    private boolean verifySignature(String signature, String basestr) {
        try {

            MessageDigest digester = MessageDigest.getInstance("SHA-1");
            byte[] digest = digester.digest(basestr.getBytes());
            return Arrays.equals(signature.getBytes(), encodeHex(digest));
        } catch (NoSuchAlgorithmException e) {
            throw Langs.toUncheck(e);
        }
    }

    private byte[] encodeHex(byte[] digest) {
        int l = digest.length;
        char[] out = new char[l << 1];
        // two characters form the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = DIGITS_UPPER[(0xF0 & digest[i]) >>> 4];
            out[j++] = DIGITS_UPPER[0x0F & digest[i]];
        }

        return new String(out).getBytes();
    }
}
